/* Original Author:  Tao Klerks
 * Date: 2007   tao@klerks.biz     --Originally intended for use with DOMercury:  http://www.odierno.com/domercury
 * Description: Handles serialization / deserialization, including of serializable custom types 
 *   as long as the type is loaded (does not work for plugins that are only loaded using reflection).
 *
 * --CHANGELOG--
 * 2008-05-21 - Tao Klerks:
 *  - Moved into new more appropriate namespace
 * 
 * */
using System;
using System.Collections.Generic;
using System.Text;
namespace DOMercury.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml.Serialization;
    [XmlRoot("dictionary")]
    public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();
            if (wasEmpty)
                return;
            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                reader.ReadStartElement("item");
                reader.ReadStartElement("key");
                TKey key = (TKey)keySerializer.Deserialize(reader);
                reader.ReadEndElement();

                TValue value;
                String typeString = reader.GetAttribute("type");
                reader.ReadStartElement("value");
                if (typeString != null)
                {
                    Type valueType = Type.GetType(typeString);
                    //if the serialized derived type exists, get the data - otherwise throw exception.
                    if (valueType != null)
                    {
                        value = (TValue)new XmlSerializer(valueType).Deserialize(reader);
                    }
                    else
                    {
                        throw new Exception("Deserialization of derived type failed in SerializableDictionary class; the type could not be retrieved from executing assemblies using static method Type.GetType(). Unknown Type: " + typeString);
                    }
                }
                else
                {
                    value = (TValue)valueSerializer.Deserialize(reader);
                }
                reader.ReadEndElement();

                this.Add(key, value);

                reader.ReadEndElement();
                reader.MoveToContent();
            }
            reader.ReadEndElement();
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
            XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
            foreach (TKey key in this.Keys)
            {
                writer.WriteStartElement("item");
                writer.WriteStartElement("key");
                keySerializer.Serialize(writer, key);
                writer.WriteEndElement();
                writer.WriteStartElement("value");
                TValue value = this[key];

                Type valueType = value.GetType();
                if (valueType.IsSubclassOf(typeof(TValue)))
                {
                    writer.WriteAttributeString("type", valueType.ToString());
                    new XmlSerializer(valueType).Serialize(writer, value);
                }
                else
                {
                    valueSerializer.Serialize(writer, value);
                }

                writer.WriteEndElement();
                writer.WriteEndElement();
            }
        }
        #endregion
    }
}
